using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace WMVConvert
{
	public class SetMetadata: Task
	{
		private WMVConvert.Metadata.Editor editor;
		private ITaskItem[] videoInput;

		[Required]
		public ITaskItem[] InputVideo
		{
			get
			{
				return videoInput;
			}
			set
			{
				videoInput = value;
			}
		}

		bool force;

		public bool Force
		{
			get { return force; }
			set { force = value; }
		}

		List<ITaskItem> outputVideo;

		[Output]
		public ITaskItem[] OutputVideo
		{
			get
			{
				return outputVideo.ToArray();
			}
			set
			{
				outputVideo.Clear();
				outputVideo.AddRange(value);
			}
		}

		private TaskLoggingHelper log;

		public new TaskLoggingHelper Log
		{
			get
			{
				Task task = this;
				if (task.Log == null)
				{
					return log;
				}
				return task.Log;
			}
		}

		public SetMetadata(TaskLoggingHelper log)
			:this()
		{
			this.log = log;
		}

		public SetMetadata()
		{
			this.editor = null;
			this.force = false;
			this.outputVideo = new List<ITaskItem>();
		}

		public override bool Execute()
		{
			foreach (ITaskItem item in videoInput)
			{
				TaskItem newItem = null;
				try
				{
					Log.LogMessage("Setting attributes for media file {0}", item.ItemSpec);
					using (this.editor = new Metadata.Editor(item.ItemSpec))
					{
                        IDictionary<string, object> metadata = SearchForMetadata(item.ItemSpec);
                        CopyMetadata(metadata);
						editor.Save();
						editor.Close();
					}
					this.editor = null;
					newItem = new TaskItem(item);
				}
				catch (Exception e)
				{
					Log.LogErrorFromException(e);
				}
				finally
				{
					if (newItem != null)
					{
						outputVideo.Add(newItem);
					}
					Log.LogMessage("Setting attributes ended", item.ItemSpec);
				}
			}
			return true;
		}

		private void CopyMetadata(IDictionary<string, object> source)
		{
			foreach (string key in source.Keys)
			{
				Metadata.WMAttribute wmAttrib = editor.Attributes[key];
				if (wmAttrib != null)
				{
					IDictionary<Metadata.WMAttribute, object> target = editor;
					object value = source[wmAttrib.Name];
					try
					{
						if (!target.ContainsKey(wmAttrib))
						{
							Log.LogMessage("	Adding attribute {0}=\"{1}\"", key, value);
							target.Add(wmAttrib, value);
						}
						else
						{
							object currentValue = target[wmAttrib];
							if (!Force && currentValue!=null)
							{
								Log.LogMessage("	Attribute {0} already exist with value \"{1}\", not replacing with value \"{2}\"", key, currentValue, value);
							}
							else
							{
								Log.LogMessage("	Changing attribute {0}=\"{1}\" from old value \"{2}\"", key, value, currentValue);
								target[wmAttrib] = value;
							}
						}
					}
					catch (Exception e)
					{
						Log.LogErrorFromException(e);
					}
				}
			}
		}

		private IDictionary<string, object> SearchForMetadata(string inputFile)
		{
			IDictionary<string, object> metadata = new Dictionary<string, object>();
			FileInfo fi = new FileInfo(inputFile);
			DirectoryInfo di = fi.Directory;
			while (di != null)
			{
				SearchForMetadata(di,ref metadata);
				di = di.Parent;
			}
			SearchForMetadata(fi,ref metadata);
			SearchForPicture(fi, ref metadata);
			return metadata;
		}

		internal static class FileNameSearches
		{
			public static readonly Regex MovieWithYear = new Regex(@"\s*(?<Title>.*\S)\s*(\((?<Year>\d{4,4})\))\.*", RegexOptions.CultureInvariant);
			public static readonly Regex TitleSubTitle = new Regex(@"\s*(?<Title>.*\S)\s*-\s*(?<SubTitle>.*\S)\s*", RegexOptions.CultureInvariant);
			public static readonly Regex TVShowName = new Regex(@"\s*(?<Title>.*\S)\s*-?\s*(?<Season>\d{1,2})x(?<Episode>\d{1,2})\s*-?\s*(?<EpisodeTitle>.*\S)\s*", RegexOptions.CultureInvariant);
		}

		private void AddMetadata(ref IDictionary<string, object> metadata, string key, object value)
		{
			if (!metadata.ContainsKey(key))
			{
				metadata.Add(key, value);
			}
		}

		public void SearchForPicture(FileInfo fi, ref IDictionary<string, object> metadata)
		{
			string pictureFileName = Path.GetFileNameWithoutExtension(fi.FullName) + ".jpg";
			pictureFileName = Path.Combine(fi.Directory.FullName, pictureFileName);
			if (!File.Exists(pictureFileName))
			{
				pictureFileName = Path.Combine(fi.Directory.FullName, "mymovies-front.jpg");
			}
			if (File.Exists(pictureFileName))
			{
                AddMetadata(ref metadata, editor.Attributes.WMPicture.Name, new Metadata.WMPicture(pictureFileName));
			}
		}

		public void SearchForMetadata(FileInfo fi,ref IDictionary<string,object> metadata)
		{
			string title = Path.GetFileNameWithoutExtension(fi.Name).Trim();
			title = title.Replace("_", " ");
			title = title.Replace(".", " ");
			Match matchMovie = FileNameSearches.MovieWithYear.Match(title);
			Match matchTitleSubTitle = FileNameSearches.TitleSubTitle.Match(title);
			Match matchTVShowName = FileNameSearches.TVShowName.Match(title);

            Metadata.WMMediaKindValue mediaKind = null;
			if (metadata.ContainsKey(editor.Attributes.WMMediaClassSecondaryID.Name))
			{
                mediaKind = (Metadata.WMMediaKindValue)metadata[editor.Attributes.WMMediaClassSecondaryID.Name];
			}
			string year = matchMovie.Groups["Year"].Value;
			if (!string.IsNullOrEmpty(year))
			{
				AddMetadata(ref metadata,editor.Attributes.WMYear.Name, year);
			}
			string episodeNumber = matchTVShowName.Groups["Episode"].Value;
			if (!string.IsNullOrEmpty(episodeNumber))
			{
				if (mediaKind.Value == Metadata.MediaKind.TVShow)
				{
					AddMetadata(ref metadata, editor.Attributes.WMEpisodeNumber.Name, episodeNumber);
				}
				else
				{
					AddMetadata(ref metadata, editor.Attributes.WMTrackNumber.Name, episodeNumber);
				}
			}
			string subTitle = string.Empty;
			if (!string.IsNullOrEmpty(matchMovie.Groups["Title"].Value))
			{
				title = matchMovie.Groups["Title"].Value;
			}
			else if (!string.IsNullOrEmpty(matchTVShowName.Groups["Title"].Value))
			{
				if (metadata.ContainsKey(editor.Attributes.WMTitle.Name))
				{
					title = metadata[editor.Attributes.WMTitle.Name].ToString();
				}
				else
				{
					title = matchTVShowName.Groups["Title"].Value;
				}
				subTitle = matchTVShowName.Groups["EpisodeTitle"].Value;
			}
			else if (!string.IsNullOrEmpty(matchTitleSubTitle.Groups["Title"].Value))
			{
				title = matchTitleSubTitle.Groups["Title"].Value;
				subTitle = matchTitleSubTitle.Groups["SubTitle"].Value;
			}
			if (!string.IsNullOrEmpty(episodeNumber) && !string.IsNullOrEmpty(subTitle))
			{
				subTitle = episodeNumber + " " + subTitle;
			}
			if (mediaKind.Value == Metadata.MediaKind.MusicVideo)
			{
				if (!string.IsNullOrEmpty(title))
				{
					int track = 0;
					if (int.TryParse(title, out track))
					{
						AddMetadata(ref metadata, editor.Attributes.WMTrackNumber.Name, title);
					}
					else if (!metadata.ContainsKey(editor.Attributes.WMAuthor.Name))
					{
						AddMetadata(ref metadata, editor.Attributes.WMAuthor.Name, title);
					}
				}
				if (!string.IsNullOrEmpty(subTitle))
				{
					AddMetadata(ref metadata, editor.Attributes.WMTitle.Name, subTitle);
				}
			}
			else
			{
				if (!string.IsNullOrEmpty(title))
				{
					AddMetadata(ref metadata, editor.Attributes.WMTitle.Name, title);
				}
				if (!string.IsNullOrEmpty(subTitle))
				{
					AddMetadata(ref metadata, editor.Attributes.WMSubTitle.Name, subTitle);
				}
				else if (Force)
				{
					AddMetadata(ref metadata, editor.Attributes.WMSubTitle.Name, string.Empty);
				}
			}
		}

		private void Add(IDictionary<string, object> metadata,string key,object value)
		{
		}

		public void SearchForMetadata(DirectoryInfo di,ref IDictionary<string,object> collectedValues)
		{
			string metadataFile = Path.Combine(di.FullName, ".metadata");
			if (File.Exists(metadataFile))
			{
				using (StreamReader reader = File.OpenText(metadataFile))
				{
					while (!reader.EndOfStream)
					{
						string line = reader.ReadLine();
						int equalIndex = line.IndexOf("=");
						if (equalIndex != -1)
						{
							string key = line.Substring(0, equalIndex).Trim();
							Metadata.WMAttribute wmAttrib = editor.Attributes[key];
							if (wmAttrib!=null)
							{
								string value = line.Substring(equalIndex + 1).Trim();
								if (wmAttrib.SubType != null)
								{
                                    string curDir = System.IO.Directory.GetCurrentDirectory();
                                    try
                                    {
                                        System.IO.Directory.SetCurrentDirectory(di.FullName);
                                        Metadata.IWMBinary binPropValue = wmAttrib.CreateBinaryFromString(value);
                                        if (binPropValue != null)
                                        {
                                            AddMetadata(ref collectedValues, key, binPropValue);
                                        }
                                    }
                                    finally
                                    {
                                        System.IO.Directory.SetCurrentDirectory(curDir);
                                    }
								}
								else
								{
									AddMetadata(ref collectedValues, key, value);
								}
							}
						}
					}
				}
			}
		}
	}
}
